/*
 * 描          述:  <描述>
 * 修  改   人:  Administrator
 * 修改时间:  2016年11月6日
 * <修改描述:>
 */
package com.tx.core.ddlutil.builder.alter;

import com.tx.core.common.PrioritySupportComparator;
import com.tx.core.ddlutil.builder.AbstractDDLBuilder;
import com.tx.core.ddlutil.model.TableColumnDef;
import com.tx.core.ddlutil.model.TableDef;
import com.tx.core.exceptions.SILException;
import com.tx.core.exceptions.util.AssertUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * 抽象的修改表DDL构建器<br/>
 * <功能详细描述>
 * 
 * @author  Administrator
 * @version  [版本号, 2016年11月6日]
 * @see  [相关类/方法]
 * @since  [产品/模块版本]
 */
public abstract class AbstractAlterTableDDLBuilder
        extends AbstractDDLBuilder<AlterTableDDLBuilder>
        implements AlterTableDDLBuilder, AlterTableDDLBuilderFactory {
    
    /** 待修改表 */
    private final TableDef sourceTable;
    
    /** <默认构造函数> */
    public AbstractAlterTableDDLBuilder() {
        super();
        
        this.sourceTable = null;
    }
    
    /** <默认构造函数> */
    public AbstractAlterTableDDLBuilder(TableDef sourceTable) {
        super(sourceTable.getName());
        
        AssertUtils.notNull(sourceTable, "sourceTable is null.");
        AssertUtils.notEmpty(sourceTable.getName(),
                "sourceTable.tableName is empty.");
        AssertUtils.notEmpty(sourceTable.getColumns(),
                "sourceTable.columns is empty.");
        
        this.sourceTable = sourceTable;
    }
    
    /** <默认构造函数> */
    public AbstractAlterTableDDLBuilder(TableDef newTable, TableDef sourceTable) {
        super(newTable);
        
        AssertUtils.notNull(newTable, "newTable is null.");
        AssertUtils.notEmpty(newTable.getName(),
                "newTable.tableName is empty.");
        AssertUtils.notNull(sourceTable, "sourceTable is null.");
        AssertUtils.notEmpty(sourceTable.getName(),
                "sourceTable.tableName is empty.");
        AssertUtils.isTrue(
                newTable.getName()
                        .equalsIgnoreCase(sourceTable.getName()),
                "newTable.tableName:{} should equalsIgnoreCase sourceTable.tableName:{}",
                new Object[] { newTable.getName(),
                        sourceTable.getName() });
        
        this.sourceTable = sourceTable;
    }
    
    /**
     * @return
     */
    @Override
    public AlterTableComparetor compare() {
        AlterTableComparetor comparetor = new AlterTableComparetor();
        comparetor.setSourceTableDef(this.sourceTable);
        comparetor.setPrimaryKey(this.getPrimaryKey());
        comparetor.setTargetTableColumns(this.columns);
        comparetor.setTargetTableIndexes(this.indexes);
        comparetor.compare();
        
        return comparetor;
    }
    
    /**
     * @return
     */
    @Override
    public final String alterSql() {
        AlterTableComparetor comparetor = new AlterTableComparetor();
        comparetor.setSourceTableDef(this.sourceTable);
        comparetor.setPrimaryKey(this.getPrimaryKey());
        comparetor.setTargetTableColumns(this.columns);
        comparetor.setTargetTableIndexes(this.indexes);
        comparetor.compare();
        
        try {
            doBuildAlterSql(comparetor);
        } catch (IOException e) {
            throw new SILException("generate alter table sql exception.", e);
        }
        
        String alterSql = this.writer.toString();
        
        return alterSql;
    }
    
    /**
      * 构建修改表的Sql
      * <功能详细描述>
      * @param comparetor
      * @throws IOException [参数说明]
      * 
      * @return void [返回类型说明]
      * @exception throws [异常类型] [异常说明]
      * @see [类、类#方法、类#成员]
     */
    protected void doBuildAlterSql(AlterTableComparetor comparetor)
            throws IOException {
        this.writer = new StringWriter();
        
        if (!comparetor.isNeedAlter()) {
            return;
        }

        //输出修改表注释
        writeAlterTableComment();
        if(CollectionUtils.isNotEmpty(comparetor.getAlterTableColumns())){
            //写入修改表开始
            //writeAlterStartStmt();
            //增加字段
            writeAlterAddColumnsStmt(comparetor.getAlterTableColumns());
            //修改字段
            writeAlterModifyColumnsStmt(comparetor.getAlterTableColumns());
            //写入修改表结束
            //writeAlterEndStmt();
        }
        //删除主键
        if (comparetor.isNeedModifyPrimaryKey()) {
            if(StringUtils.isNotEmpty(this.sourceTable.getPrimaryKey()) && StringUtils.isNotBlank(this.sourceTable.getPrimaryKey())){
                //如果主键需要编辑，并且之前的主键存在，则先删除主键
                writeAlterDropPrimaryKey();
            }
            if(StringUtils.isNotEmpty(this.getPrimaryKey()) && StringUtils.isNotBlank(this.getPrimaryKey())){
                //新的主键存在，则新增主键
                writeAlterAddPrimaryKey(comparetor.getPrimaryKey());
            }
        }
        //删除索引: drop需要删除的和被修改过的
        writeAlterDropIndex(comparetor.getAlterTableIndexes());
        //增加索引: 增加新增的，和被修改过的
        writeAlterAddIndexesStmt(comparetor.getAlterTableIndexes());
        

    }
    
    /**
     * 写入修改表:
     *     //Alter Table xxx
     * @throws IOException [参数说明]
     * 
     * @return void [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
    */
    protected void writeAlterStartStmt() throws IOException {
        //写入修修改表：加字段的注释
        print("ALTER TABLE ");
        print(this.tableName);
        println(" ");//输出括号后换行
    }
    
    /**
      * 写入结束段<br/>
      * <功能详细描述>
      * @throws IOException [参数说明]
      * 
      * @return void [返回类型说明]
      * @exception throws [异常类型] [异常说明]
      * @see [类、类#方法、类#成员]
     */
    protected void writeAlterEndStmt() throws IOException {
        deleteLastIndexOf(",");//删除最后一个,
        //写入修修改表：加字段的注释
        println(";");
    }
    
    /**
      * 写入删除主键<br/>
      * <功能详细描述>
      * @throws IOException [参数说明]
      * 
      * @return void [返回类型说明]
      * @exception throws [异常类型] [异常说明]
      * @see [类、类#方法、类#成员]
     */
    protected void writeAlterDropPrimaryKey() throws IOException {
        //String primaryKeyName
        //        if (!isNeedDropPrimaryKey) {
        //            return;
        //        }
        //AssertUtils.notEmpty(primaryKeyName, "primaryKeyName is empty.");
        //print("   DROP CONSTRAINT ");//输出缩进
        //print(primaryKeyName);
        //println(",");
        //pgsql:ALTER TABLE task_def DROP CONSTRAINT "task_def_pkey";
        println("ALTER TABLE " + this.tableName + " DROP PRIMARY KEY;");
    }
    
    /**
     * 写入删除主键<br/>
     * <功能详细描述>
     * @param primaryKeyColumnNames
     * @throws IOException [参数说明]
     * 
     * @return void [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
    */
    protected void writeAlterAddPrimaryKey(String primaryKeyColumnNames)
            throws IOException {
        if (StringUtils.isEmpty(primaryKeyColumnNames)) {
            return;
        }
        
        /*print("   ADD PRIMARY KEY (");//输出缩进
        print(primaryKeyColumnNames);
        println("),");*/
        println("ALTER TABLE " + this.tableName + " ADD PRIMARY KEY (" + primaryKeyColumnNames + ");");
    }
    
    /**
     * 增加字段<br/>
     * <功能详细描述>
     * @throws IOException [参数说明]
     * 
     * @return void [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    protected void writeAlterAddColumnsStmt(
            List<AlterTableColumn> alterTableColumns) throws IOException {
        List<TableColumnDef> addColumns = new ArrayList<>();
        for (AlterTableColumn col : alterTableColumns) {
            if (!col.isNeedAlter()) {
                continue;
            }
            if (!AlterTypeEnum.ADD.equals(col.getAlterType())) {
                continue;
            }
            addColumns.add(col.getTargetColumn());
        }
        
        if (CollectionUtils.isEmpty(addColumns)) {
            return;
        }
        PrioritySupportComparator.sort(addColumns);
        for (TableColumnDef column : addColumns) {
            print("ALTER TABLE ");//输出缩进
            print(this.tableName);//输出缩进
            print(" ADD COLUMN ");//输出缩进
            writeColumn(column);
            println(";");
        }
    }
    
    /**
     * 修改字段
     * <功能详细描述>
     * @throws IOException [参数说明]
     * 
     * @return void [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    protected void writeAlterModifyColumnsStmt(
            List<AlterTableColumn> alterTableColumns) throws IOException {
        List<TableColumnDef> modifyColumns = new ArrayList<>();
        for (AlterTableColumn col : alterTableColumns) {
            if (!col.isNeedAlter()) {
                continue;
            }
            if (!AlterTypeEnum.MODIFY.equals(col.getAlterType())
                    && !AlterTypeEnum.MODIFY_TO_NULLABLE
                            .equals(col.getAlterType())) {
                continue;
            }
            if (col.getTargetColumn() != null) {
                modifyColumns.add(col.getTargetColumn());
            } else {
                col.getSourceColumn().setRequired(false);
                modifyColumns.add(col.getSourceColumn());
            }
        }
        
        if (CollectionUtils.isEmpty(modifyColumns)) {
            return;
        }
        
        PrioritySupportComparator.sort(modifyColumns);
        for (TableColumnDef column : modifyColumns) {
            doWriteModifyColumn(column);
        }
    }

    /**
     * 写入编辑字段语句
     * @param column
     * @throws IOException
     */
    protected void doWriteModifyColumn(TableColumnDef column) throws IOException {
        print("ALTER TABLE ");//输出缩进
        print(this.tableName);//输出缩进
        print(" MODIFY COLUMN ");//输出缩进
        writeColumn(column);
        println(";");
    }
    
    /**
     * 写入表字段<br/>
     * <功能详细描述>
     * @param column
     * @throws IOException [参数说明]
     * 
     * @return void [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
    */
    protected void writeColumn(TableColumnDef column) throws IOException {
        print(column.getName());
        print(" ");
        print(column.getColumnType(this.getDDLDialect()));
        
        if (!StringUtils.isEmpty(column.getDefaultValue())) {
            print(" DEFAULT ");
            print(column.getDefaultValue());
        }
        
        if (column.isRequired()) {
            print(" NOT NULL");
        }
    }
    
    /**
     * 写入删除主键<br/>
     * <功能详细描述>
     * @param alterTableIndexComparetors
     * @throws IOException [参数说明]
     * 
     * @return void [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
    */
    protected void writeAlterDropIndex(List<AlterTableIndexComparetor> alterTableIndexComparetors)
            throws IOException {
        if (CollectionUtils.isEmpty(alterTableIndexComparetors)) {
            return;
        }
        
        for (AlterTableIndexComparetor index : alterTableIndexComparetors) {
            if (!index.isNeedAlter()) {
                continue;
            }
            if(AlterTypeEnum.DROP.equals(index.getAlterType())
                || AlterTypeEnum.MODIFY.equals(index.getAlterType())){
                doWriteAlterDropIndex(index.getIndexName());
            }
        }
    }

    /**
     * 删除索引
     * @param indexName
     */
    protected void doWriteAlterDropIndex(String indexName) throws IOException {
        //DROP INDEX IDX_TASK_DEF_01 ON task_def;
        print("DROP INDEX ");//输出缩进
        print(indexName);
        print(" ON ");
        print(this.tableName);
        println(";");
    }
    
    /**
     * 写入索引创建语句<br/>
     * <功能详细描述>
     * @throws IOException [参数说明]
     * 
     * @return void [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
    */
    protected void writeAlterAddIndexesStmt(
            List<AlterTableIndexComparetor> alterTableIndexComparetors) throws IOException {
        if (CollectionUtils.isEmpty(alterTableIndexComparetors)) {
            return;
        }
        
        for (AlterTableIndexComparetor index : alterTableIndexComparetors) {
            if (!index.isNeedAlter()) {
                continue;
            }
            if(AlterTypeEnum.ADD.equals(index.getAlterType())
                || AlterTypeEnum.MODIFY.equals(index.getAlterType())){
                doWriteAlterAddIndex(index.getTargetIndex().isUnique(),index.getTargetIndex().getName(),index.getTargetIndex().getColumnNames());
            }
        }
    }

    /**
     * 删除索引
     * @param indexName
     */
    protected void doWriteAlterAddIndex(boolean unique,String indexName,String columnNames) throws IOException {
        if (unique) {
            print("CREATE UNIQUE INDEX ");
            print(indexName);
            print(" ON ");
            print(this.tableName);
            print("(");
            print(columnNames);
            println(");");
        } else {
            print("CREATE INDEX ");
            print(indexName);
            print(" ON ");
            print(this.tableName);
            print("(");
            print(columnNames);
            println(");");
        }
    }
    
    /** 
     * 写入表注释信息<br/>
     * @param
     */
    protected void writeAlterTableComment() throws IOException {
        printComment("alter talbe " + this.tableName);
    }
    
    /**
     * @return 返回 sourceTable
     */
    @Override
    public TableDef sourceTable() {
        return sourceTable;
    }
}
